// 声明一个类,然后先取个名字
class TinyEmitter {
    constructor() {
        //声明一个对象,专门存储订阅者(使用$on注册的函数)
        this.subs = {}
    }

    //注册事件的方法
    //type是事件类型 是一个字符串strng
    //cb事件对应的处理函数 function
    //从设计模式的角度 on属于订阅subscribe的动作
    $on(type, cb) {
        if (typeof type === 'string' && typeof cb === 'function') {
            //数据传递是正确的
            //判断subs中是否存在这个类型,如果存在,那么就往subs中push回调函数cb
            //如果不存在,那么就要创建这条属性 并且把值设置为空数组 还要把cb push进去
            if (!this.subs[type]) {
                this.subs[type] = []
            }
            this.subs[type].push(cb)
        } else {
            throw Error('type must be string and cb must be a function')
        }
    }

    //卸载事件的方法
    //设计模式来说,移除订阅者
    $off(type, cb) {
        //两个参数缺一不可
        //type:移除事件类型
        //cb:移除的事件处理函数
        if (typeof type === 'string' && typeof cb === 'function') {
            //把订阅者对应的数组取出来
            let subs = this.subs[type];
            //遍历这个数组
            for (var i = subs.length - 1; i >= 0; i--) {
                //找到了要移除掉的函数
                if (cb === subs[i]) {
                    subs.splice(i, 1)
                }
            }
        } else {
            throw Error('type must be string and cb must be a function')
        }
    }

    //触发事件的方法
    //在设计模式上来说emit属于发布publish动作
    $emit(...args) {
        //触发的方法中(发布的方法)没有传递参数,抛出异常
        if (args.length === 0) {
            //最少要传递一个类型参数进来
            throw Error('paramters 1th type must be required')
        }
        //类型是传递的第一个参数
        let type = args.splice(0, 1);
        //通知所有的订阅者
        this.subs[type].forEach(fn => {
            //说白了就是把订阅者的回调函数都调用一遍
            fn.apply(this, args)
        })
    }
}




